﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;

namespace TARLib
{
    public class PuntoDistribuzione
    {
        public int PuntoDistribuzioneID { get; set; }
        public string Descrizione { get; set; }
        public bool IsCheckInRichiesto { get; set; }
        public bool IsStampaRemotaRichiesta { get; set; }
        public bool IsConsegnaRichiesta { get; set; }
        public bool IsTavoloRichiesto { get; set; }
        public string PathStampante { get; set; }
        public int VelocitaWidthFinestraSecondi { get; set; }
        public int VelocitaNSuddivisioni { get; set; }
        public int VelocitaLeftSkipPercentage { get; set; }
        public int VelocitaRightSkipPercentage { get; set; }
        public int GracePeriodStampaSecondi { get; set; }
        public int? VelocitaFissaProdottiOra { get; set; }

        public PuntoDistribuzione(int puntoDistribuzioneID, string descrizione, bool isCheckInRichiesto, bool isStampaRemotaRichiesta, bool isConsegnaRichiesta, bool isTavoloRichiesto, string pathStampante, int velocitaWidthFinestraSecondi, int velocitaNSuddivisioni, int velocitaLeftSkipPercentage, int velocitaRightSkipPercentage, int gracePeriodStampaSecondi, int? velocitaFissaProdottiOra)
        {
            PuntoDistribuzioneID = puntoDistribuzioneID;
            Descrizione = descrizione;
            IsCheckInRichiesto = isCheckInRichiesto;
            IsStampaRemotaRichiesta = isStampaRemotaRichiesta;
            IsConsegnaRichiesta = isConsegnaRichiesta;
            PathStampante = pathStampante;
            VelocitaWidthFinestraSecondi = velocitaWidthFinestraSecondi;
            VelocitaNSuddivisioni = velocitaNSuddivisioni;
            VelocitaLeftSkipPercentage = velocitaLeftSkipPercentage;
            VelocitaRightSkipPercentage = velocitaRightSkipPercentage;
            IsTavoloRichiesto = isTavoloRichiesto;
            GracePeriodStampaSecondi = gracePeriodStampaSecondi;
            VelocitaFissaProdottiOra = velocitaFissaProdottiOra;
        }

        public float GetVelocita(DateTime dataOraDaValutare)
        {
            if (VelocitaFissaProdottiOra.HasValue)
            {
                return ((float)VelocitaFissaProdottiOra) / 60f;
            }
            else
            {
                DateTime dataLeft = dataOraDaValutare.Add(-new TimeSpan(0, 0, VelocitaWidthFinestraSecondi));
                DateTime dataRight = dataOraDaValutare;
                int nMaxSuddivisioni = VelocitaNSuddivisioni;
                int leftSkipPercentage = VelocitaLeftSkipPercentage;
                int rightSkipPercentage = VelocitaRightSkipPercentage;

                List<TARLib.Stats.NProdottiConsegnati> allProdottiConsegnati = GetNProdottiConsegnati(dataLeft);
                float stepInSecond = (float)(dataRight.Subtract(dataLeft).TotalSeconds / (float)nMaxSuddivisioni);
                List<float> Medie = new List<float>();
                // Calcolo velocità media
                for (int i = 0; i < nMaxSuddivisioni; i++)
                {
                    //Console.WriteLine(String.Format("{0:HH:mm:ss}-{1:HH:mm:ss} [{2} s]: {3:0} ({4:0.00} prodotti/min)",
                    //    dataOraMin.AddSeconds(stepInSecond * i),
                    //    dataOraMax,
                    //    dataOraMax.Subtract(dataOraMin.AddSeconds(stepInSecond * i)).TotalSeconds,
                    //    allProdottiConsegnati.Where(o => o.DataOraConsegna >= dataOraMin.AddSeconds(stepInSecond * i) && o.DataOraConsegna <= dataOraMax).Sum(o => o.NProdotti),
                    //    60*((float)allProdottiConsegnati.Where(o => o.DataOraConsegna >= dataOraMin.AddSeconds(stepInSecond * i) && o.DataOraConsegna <= dataOraMax).Sum(o => o.NProdotti))/(float)dataOraMax.Subtract(dataOraMin.AddSeconds(stepInSecond * i)).TotalSeconds));

                    Medie.Add(
                        60 * ((float)allProdottiConsegnati.Where(o => o.DataOraConsegna >= dataLeft.AddSeconds(stepInSecond * i) && o.DataOraConsegna <= dataRight).Sum(o => o.NProdotti)) / (float)dataRight.Subtract(dataLeft.AddSeconds(stepInSecond * i)).TotalSeconds);
                }

                try
                {
                    int leftSkip = nMaxSuddivisioni * leftSkipPercentage / 100;
                    int rightSkip = nMaxSuddivisioni * rightSkipPercentage / 100;
                    float leftMediaSkip = Medie.OrderBy(o => o).Skip(leftSkip).First();
                    float rightMediaSkip = Medie.OrderByDescending(o => o).Skip(rightSkip).First();

                    return Medie.Where(o => o > leftMediaSkip && o < rightMediaSkip).Average();
                }
                catch (Exception)
                {
                    return Medie.Average();
                }
            }
        }

        public List<Stats.NProdottiInCoda> GetNProdottiInCoda(int deltaEscusioneMinuti)
        {
            List<Stats.NProdottiInCoda> retList = new List<Stats.NProdottiInCoda>();

            SqlConnection conn = new SqlConnection(Database.GetDBConnectionString());
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = conn;
            cmd.CommandText = "[Cucina].[SP_Stats_CodaProdotti]";
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.Parameters.Add(new SqlParameter("@PuntoDistribuzioneID", System.Data.SqlDbType.Int) { Value = PuntoDistribuzioneID });
            cmd.Parameters.Add(new SqlParameter("@DeltaEsclusioneMinuti", System.Data.SqlDbType.Int) { Value = deltaEscusioneMinuti });

            SqlDataReader dr = null;
            try
            {
                conn.Open();
                dr = cmd.ExecuteReader();

                while (dr.Read())
                {
                    retList.Add(new Stats.NProdottiInCoda(
                        (DateTime)dr["DataOra"],
                        (int)dr["ProdottoID"],
                        (int)dr["NProdotti"]));
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (dr != null)
                {
                    dr.Close();
                    dr.Dispose();
                }
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }

            return retList;
        }

        public TimeSpan? GetLunghezzaCoda(DateTime dataOraDaValutare)
        {
            // Quanto c'è di coda?
            // Quanti prodotti ci sono in coda
            float velocita = GetVelocita(dataOraDaValutare);
            if (velocita != 0)
            {
                return new TimeSpan(
                    0,
                    (int)(GetNProdottiInCoda(30).Sum(o => o.NProdotti) / velocita),
                    0);
            }
            else
            {
                return null;
            }
        }

        public List<Stats.NProdottiConsegnati> GetNProdottiConsegnati(DateTime dataOraMin)
        {
            List<Stats.NProdottiConsegnati> retList = new List<Stats.NProdottiConsegnati>();

            SqlConnection conn = new SqlConnection(Database.GetDBConnectionString());
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = conn;
            cmd.CommandText = "[Cucina].[SP_Stats_ProdottiDistribuiti]";
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.Parameters.Add(new SqlParameter("@PuntoDistribuzioneID", System.Data.SqlDbType.Int) { Value = PuntoDistribuzioneID });
            cmd.Parameters.Add(new SqlParameter("@DataOraMin", System.Data.SqlDbType.DateTime) { Value = dataOraMin });

            SqlDataReader dr = null;
            try
            {
                conn.Open();
                dr = cmd.ExecuteReader();

                while (dr.Read())
                {
                    retList.Add(new Stats.NProdottiConsegnati(
                        (DateTime)dr["DataOra"],
                        (int)dr["NProdotti"]));
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (dr != null)
                {
                    dr.Close();
                    dr.Dispose();
                }
                cmd.Dispose();
                conn.Close();
                conn.Dispose();
            }

            return retList;
        }

        public List<Stats.FinestraProdotto> GetFinestreProdotto(int monitorFinestraWidthInMinutes, int monitorNFinestre, DateTime dataOraDaConsiderare)
        {
            // La finestra 0 è l'area Check-in, le finestre 1-monitorNFinestre sono calcolate con la velocità di distribuzione, la finestra (monitorNFinestre+1) è quella delle comande "ritardatarie"
            List<TARLib.Stats.FinestraProdotto> retValue = new List<TARLib.Stats.FinestraProdotto>();
            float VelocitaCorrente = GetVelocita(dataOraDaConsiderare);
            if (((int)(VelocitaCorrente * monitorFinestraWidthInMinutes) > 0))
            {
                int nProdottiPerFinestra = (int)(VelocitaCorrente * monitorFinestraWidthInMinutes);
                int nProdottiCounter = 0;
                int finestraCorrente = 0;
                foreach (TARLib.Stats.NProdottiInCoda p in GetNProdottiInCoda(30).OrderBy(o => o.DataOraComanda))
                {
                    nProdottiCounter += p.NProdotti;
                    finestraCorrente = (int)(nProdottiCounter / nProdottiPerFinestra) + 1;

                    if (finestraCorrente < monitorNFinestre)
                    {
                        if (retValue.Any(o => o.ProdottoID == p.ProdottoID && o.Finestra == finestraCorrente))
                        {
                            // C'è già la finestra!
                            retValue.Where(o => o.ProdottoID == p.ProdottoID && o.Finestra == finestraCorrente).FirstOrDefault().NProdotti += p.NProdotti;
                        }
                        else
                        {
                            // Nuova finestra
                            retValue.Add(new TARLib.Stats.FinestraProdotto(
                                p.ProdottoID,
                                finestraCorrente,
                                dataOraDaConsiderare.AddMinutes(monitorFinestraWidthInMinutes * finestraCorrente),
                                dataOraDaConsiderare.AddMinutes(monitorFinestraWidthInMinutes * (finestraCorrente + 1)),
                                p.NProdotti));
                        }
                    }
                }
            }

            // Finestre "check-in"
            foreach (Stats.NProdottiInCheckIn p in Management.GetNProdottiInCheckIn(this))
            {
                retValue.Add(new Stats.FinestraProdotto(p.ProdottoID, 0, null, null, p.NProdotti));
            }

            return retValue;
        }
    }
}